package org.bozo.im.service.protocols.xmpp.stanza.generator;

import java.util.ArrayList;
import java.util.Hashtable;

import org.bozo.im.service.protocols.xmpp.stanza.Stanza;
import org.bozo.im.service.protocols.xmpp.util.namespace.Namespace;

public final class StanzaGenerator {

	private ArrayList<Namespace> mNamespaces = new ArrayList<Namespace>();
	private ArrayList<StanzaGenerator> mChildren = new ArrayList<StanzaGenerator>();
	private Hashtable<NodeID, Object> mAttributes = new Hashtable<NodeID, Object>();
	private Namespace mDefaultNamespace;
	private String mContent;
	
	private Stanza mStanza;

	public StanzaGenerator(Stanza pStanza) {

		mStanza = pStanza;
		mNamespaces.add(pStanza.getNamespace());
	}

	public void setDefaultNamespace(Namespace pNamespace) {
		if (!mNamespaces.contains(pNamespace))
			mNamespaces.add(pNamespace);
		mDefaultNamespace = pNamespace;
	}

	public void addNamespace(Namespace pNamespace) {
		mNamespaces.add(pNamespace);
	}

	public void setContent(String pContent) {
		mContent = pContent;
	}
	
	public void addChild(StanzaGenerator pStanzaGenerator) {
		mChildren.add(pStanzaGenerator);
	}

	public void setAttribute(Namespace pNamespace, String pName, Object pValue) {
		if (pValue == null)
			return;
		
		NodeID lNode = new NodeID();
		lNode.mName = pName;
		lNode.mNamespace = pNamespace;

		mAttributes.put(lNode, pValue);
	}

	public void toXml(StringBuilder pBuilder) {

		if (mChildren.isEmpty() && (mContent == null || mContent.length() == 0)) {
			pBuilder.append("<");
			addOpeningTagContent(pBuilder);
			pBuilder.append("/>");
		} else {
			toXmlOpeningTag(pBuilder);

			for (StanzaGenerator lChild : mChildren)
				lChild.toXml(pBuilder);

			pBuilder.append(mContent == null ? "" : mContent);
			
			toXmlClosingTag(pBuilder);
		}
	}

	public void toXmlOpeningTag(StringBuilder pBuilder) {
		pBuilder.append("<");
		addOpeningTagContent(pBuilder);
		pBuilder.append(">");
	}

	public void toXmlClosingTag(StringBuilder pBuilder) {
		pBuilder.append("</");
		addClosingTagContent(pBuilder);
		pBuilder.append(">");
	}

	private void addOpeningTagContent(StringBuilder pBuilder) {

		Namespace lDefaultNamespace = mDefaultNamespace == null ? (mNamespaces == null || mNamespaces.isEmpty() ? null : mNamespaces.get(0)) : mDefaultNamespace;

		if (mStanza.getNamespace() != lDefaultNamespace)
			pBuilder.append(mStanza.getNamespace().getPrefix()).append(":");

		pBuilder.append(mStanza.getName());

		for(NodeID lNode : mAttributes.keySet()) {
			pBuilder.append(" ");

			if (lNode.mNamespace != lDefaultNamespace)
				pBuilder.append(lNode.mNamespace.getPrefix()).append(":");

			pBuilder.append(lNode.mName).append("='").append(mAttributes.get(lNode)).append("'");
		}

		for (Namespace lNamespace : mNamespaces) {
			pBuilder.append(" xmlns");

			if (lNamespace != lDefaultNamespace)
				pBuilder.append(":").append(lNamespace.getPrefix());

			pBuilder.append("='").append(lNamespace.getNamespace()).append("'");
		}
	}

	private void addClosingTagContent(StringBuilder pBuilder) {

		Namespace lDefaultNamespace = mDefaultNamespace == null ? (mNamespaces == null || mNamespaces.isEmpty() ? null : mNamespaces.get(0)) : mDefaultNamespace;

		if (mStanza.getNamespace() != lDefaultNamespace)
			pBuilder.append(mStanza.getNamespace().getPrefix()).append(":");

		pBuilder.append(mStanza.getName());

	}

	private class NodeID {
		Namespace mNamespace;
		String mName;

		@Override
		public boolean equals(Object pObj) {

			boolean lRes = false;

			if (pObj instanceof NodeID)
				lRes = ((NodeID)pObj).mName.equals(mName) && ((NodeID)pObj).mNamespace == mNamespace;

			return lRes;
		}
	}

}
